//Make sure jQuery has been loaded
if (typeof jQuery === "undefined") {
    throw new Error("MultiTabs requires jQuery");
}
(function ($) {
    "use strict";
    var NAMESPACE, tabIndex; //variable
    var MultiTabs,
        handler,
        getTabIndex,
        isExtUrl,
        sumDomWidth,
        trimText,
        supportStorage; //function
    var defaultLayoutTemplates, defaultInit; //default variable

    NAMESPACE = ".multitabs"; // namespace for on() function

    /**
     * splice namespace for on() function, and bind it
     * @param $selector         jQuery selector
     * @param event             event
     * @param childSelector     child selector (string), same as on() function
     * @param fn                function
     * @param skipNS            bool. If true skip splice namespace
     */
    handler = function ($selector, event, childSelector, fn, skipNS) {
        var ev = skipNS
            ? event
            : event.split(" ").join(NAMESPACE + " ") + NAMESPACE;
        $selector.off(ev, childSelector, fn).on(ev, childSelector, fn);
    };

    /**
     * get index for tab
     * @param content   content type, for 'main' tab just can be 1
     * @param capacity  capacity of tab, except 'main' tab
     * @returns int     return index
     */
    getTabIndex = function (content, capacity) {
        if (content === "main") return 0;
        capacity = capacity || 8; //capacity of maximum tab quantity, the tab will be cover if more than it
        tabIndex = tabIndex || 0;
        tabIndex++;
        tabIndex = tabIndex % capacity;
        return tabIndex;
    };

    /**
     * trim text, remove the extra space, and trim text with maxLength, add '...' after trim.
     * @param text          the text need to trim
     * @param maxLength     max length for text
     * @returns {string}    return trimed text
     */
    trimText = function (text, maxLength) {
        maxLength = maxLength || $.fn.multitabs.defaults.navTab.maxTitleLength;
        var words = (text + "").split(" ");
        var t = "";
        for (var i = 0; i < words.length; i++) {
            var w = $.trim(words[i]);
            t += w ? w + " " : "";
        }

        if (t.length > maxLength) {
            t = t.substr(0, maxLength);
            t += "...";
        }
        return t;
    };

    supportStorage = function (is_cache) {
        return !(sessionStorage === undefined) && is_cache;
    };

    /**
     * Calculate the total width
     * @param JqueryDomObjList      the object list for calculate
     * @returns {number}        return total object width (int)
     */
    sumDomWidth = function (JqueryDomObjList) {
        var width = 0;
        $(JqueryDomObjList).each(function () {
            width += $(this).outerWidth(true);
        });
        return width;
    };

    /**
     * 判断是外部URL
     * @param url          判断的URL
     * @returns {boolean}   external URL return true, local return false
     */
    isExtUrl = function (url) {
        var absUrl = (function (url) {
            var a = document.createElement("a");
            a.href = url;
            return a.href;
        })(url);
        var webRoot = window.location.protocol + "//" + window.location.host + "/";
        var urlRoot = absUrl.substr(0, webRoot.length);
        return !(urlRoot === webRoot);
    };

    /**
     * Layout Templates
     */
    defaultLayoutTemplates = {
        /**
         * Main Layout
         */
        default:
            '<div class="mt-wrapper {mainClass}" style="height: 100%;" >' +
            '<div class="mt-nav-bar {navClass}" style="background-color: {backgroundColor};">' +
            '<div class="mt-nav mt-nav-tools-left">' +
            '<ul  class="nav {nav-tabs}">' +
            "</ul>" +
            "</div>" +
            '<nav class="mt-nav mt-nav-panel">' +
            '<ul  class="nav {nav-tabs}"></ul>' +
            "</nav>" +
            '<div class="mt-nav mt-nav-tools-right">' +
            '<ul  class="nav {nav-tabs}">' +
            '<li class="mt-dropdown dropdown">' +
            '<a href="#"  class="dropdown-toggle" data-toggle="dropdown">{dropdown}<span class="caret"></span></a>' +
            '<ul role="menu" class="dropdown-menu dropdown-menu-right">' +
            '<li class="divider"></li>' +
            '<li class="mt-close-all-tabs"><a>{closeAllTabs}</a></li>' +
            '<li class="mt-close-other-tabs"><a>{closeOtherTabs}</a></li>' +
            "</ul>" +
            "</li>" +
            "</ul>" +
            "</div>" +
            "</div>" +
            '<div class="tab-content mt-tab-content " > </div>' +
            "</div>",
        navTab:
            '<a data-id="{navTabId}" class="mt-nav-tab" data-type="{type}" data-index="{index}" data-url="{url}">{title}</a>',
        closeBtn: ' <i class="mt-close-tab mdi mdi-close" style="{style}"></i>',
        ajaxTabPane:
            '<div id="{tabPaneId}" class="tab-pane {class}">{content}</div>',
        iframeTabPane:
            '<iframe id="{tabPaneId}" class="tab-pane {class}"  width="100%" height="100%" frameborder="0" src="" seamless></iframe>',
    };

    /**
     * Default init page
     * @type {*[]}
     */
    defaultInit = [
        {
            //default tabs in initial;
            type: "main", //default is info;
            title: "main", //default title;
            content:
                "<h1>Demo page</h1><h2>Welcome to use bootstrap multi-tabs :) </h2>", //default content
        },
    ];

    /**
     * multitabs constructor
     * @param element       Primary container
     * @param options       options
     * @constructor
     */
    MultiTabs = function (element, options) {
        var self = this;
        self.$element = $(element);
        self._init(options)._listen()._final();
    };

    /**
     * MultiTabs's function
     */
    MultiTabs.prototype = {
        /**
         * constructor
         */
        constructor: MultiTabs,

        /**
         * create tab and return this.
         * @param obj           the obj to trigger multitabs
         * @param active        if true, active tab after create
         * @returns this        Chain structure.
         */
        create: function (obj, active) {
            var options = this.options;
            var param, $navTab;
            if (!(param = this._getParam(obj))) {
                return this; //return multitabs obj when is invaid obj
            }
            $navTab = this._exist(param);
            if ($navTab && !param.refresh) {
                this.active($navTab);
                return this;
            }
            param.active = !param.active ? active : param.active;
            //nav tab create
            $navTab = this._createNavTab(param);
            //tab-pane create
            this._createTabPane(param);
            //add tab to storage
            this._storage(param.did, param);
            if (param.active) {
                this.active($navTab);
            }
            return this;
        },

        /**
         * Create tab pane
         * @param param
         * @param index
         * @returns {*|{}}
         * @private
         */
        _createTabPane: function (param) {
            var self = this,
                $el = self.$element;
            $el.tabContent.append(self._getTabPaneHtml(param));
            return $el.tabContent.find("#" + param.did);
        },

        /**
         * get tab pane html
         * @param param
         * @param index
         * @returns {string}
         * @private
         */
        _getTabPaneHtml: function (param) {
            var self = this,
                options = self.options;
            if (!param.content && param.iframe) {
                return defaultLayoutTemplates.iframeTabPane
                    .replace("{class}", options.content.iframe.class)
                    .replace("{tabPaneId}", param.did);
            } else {
                return defaultLayoutTemplates.ajaxTabPane
                    .replace("{class}", options.content.ajax.class)
                    .replace("{tabPaneId}", param.did)
                    .replace("{content}", param.content);
            }
        },

        /**
         * create nav tab
         * @param param
         * @param index
         * @returns {*|{}}
         * @private
         */
        _createNavTab: function (param) {
            var self = this,
                $el = self.$element;
            var navTabHtml = self._getNavTabHtml(param);
            var $navTabLi = $el.navPanelList
                .find(
                    'a[data-type="' + param.type + '"][data-index="' + param.index + '"]'
                )
                .parent("li");
            if ($navTabLi.length) {
                $navTabLi.html(navTabHtml);
                self._getTabPane($navTabLi.find("a:first")).remove(); //remove old content pane directly
            } else {
                $el.navPanelList.append("<li>" + navTabHtml + "</li>");
            }
            return $el.navPanelList.find(
                'a[data-type="' +
                param.type +
                '"][data-index="' +
                param.index +
                '"]:first'
            );
        },

        /**
         * get nav tab html
         * @param param
         * @param index
         * @returns {string}
         * @private
         */
        _getNavTabHtml: function (param) {
            var self = this,
                options = self.options;
            var closeBtnHtml, display;

            display = options.nav.showCloseOnHover ? "" : "display:inline;";
            closeBtnHtml =
                param.type === "main" ? "" : defaultLayoutTemplates.closeBtn.replace("{style}", display); //main content can not colse.
            return (
                defaultLayoutTemplates.navTab
                    .replace("{index}", param.index)
                    .replace("{navTabId}", param.did)
                    .replace("{url}", param.url)
                    .replace("{title}", param.title)
                    .replace("{type}", param.type) + closeBtnHtml
            );
        },

        /**
         * generate tab pane's id
         * @param param
         * @param index
         * @returns {string}
         * @private
         */
        _generateId: function (param) {
            return "multitabs_" + param.type + "_" + param.index;
        },

        /**
         * active navTab
         * @param navTab
         * @returns self      Chain structure.
         */
        active: function (navTab) {
            var self = this,
                $el = self.$element;
            var $navTab = self._getNavTab(navTab),
                $tabPane = self._getTabPane($navTab),
                $prevActivedTab = $el.navPanelList.find("li.active:first a");
            var prevNavTabParam = $prevActivedTab.length
                ? self._getParam($prevActivedTab)
                : {};
            var navTabParam = $navTab.length ? self._getParam($navTab) : {};
            //change storage active status
            var storage = self._storage();
            if (storage[prevNavTabParam.did]) {
                storage[prevNavTabParam.did].active = false;
            }
            if (storage[navTabParam.did]) {
                storage[navTabParam.did].active = true;
            }
            self._resetStorage(storage);
            //active navTab and tabPane
            $prevActivedTab.parent("li").removeClass("active");
            $navTab.parent("li").addClass("active");
            self._fixTabPosition($navTab);
            self._getTabPane($prevActivedTab).removeClass("active");
            $tabPane.addClass("active");
            self._fixTabContentLayout($tabPane);
            //fill tab pane
            self._fillTabPane($tabPane, navTabParam);

            return self;
        },
        /**
         * fill tab pane
         * @private
         */
        _fillTabPane: function (tabPane, param) {
            var self = this,
                options = self.options;
            var $tabPane = $(tabPane);
            //if navTab-pane empty, load content
            if (!$tabPane.html()) {
                if ($tabPane.is("iframe")) {
                    if (!$tabPane.attr("src")) {
                        $tabPane.attr("src", param.url);
                    }
                } else {
                    $.ajax({
                        url: param.url,
                        dataType: "html",
                        success: function (callback) {
                            $tabPane.html(options.content.ajax.success(callback));
                        },
                        error: function (callback) {
                            $tabPane.html(options.content.ajax.error(callback));
                        },
                    });
                }
            }
        },
        /**
         * close navTab
         * @param navTab
         * @return self     Chain structure.
         */
        close: function (navTab) {
            var self = this,
                $tabPane;
            var $navTab = self._getNavTab(navTab),
                $navTabLi = $navTab.parent("li");
            $tabPane = self._getTabPane($navTab);
            //close unsave tab confirm
            if (
                $navTabLi.length &&
                $tabPane.length &&
                $tabPane.hasClass("unsave") &&
                !self._unsaveConfirm()
            ) {
                return self;
            }
            if ($navTabLi.hasClass("active")) {
                var $nextLi = $navTabLi.next("li:first"),
                    $prevLi = $navTabLi.prev("li:last");
                //if ($nextLi.size()) {
                if ($nextLi.length) {
                    self.active($nextLi);
                    self.activeMenu($nextLi.find("a"));
                    //} else if ($prevLi.size()) {
                } else if ($prevLi.length) {
                    self.active($prevLi);
                    self.activeMenu($prevLi.find("a"));
                }
            }
            self._delStorage($navTab.attr("data-id")); //remove tab from session storage
            $navTabLi.remove();
            $tabPane.remove();
            return self;
        },

        /**
         * close others tab
         * @return self     Chain structure.
         */
        closeOthers: function (retainTab) {
            var self = this,
                $el = self.$element,
                findTab;

            if (!retainTab) {
                findTab = $el.navPanelList
                    .find("li:not(.active)")
                    .find('a:not([data-type="main"])');
            } else {
                findTab = $el.navPanelList
                    .find('a:not([data-type="main"])')
                    .filter(function (index) {
                        if (retainTab != $(this).data("index")) return this;
                    });
            }

            findTab.each(function () {
                var $navTab = $(this);
                self._delStorage($navTab.attr("data-id")); //remove tab from session storage
                self._getTabPane($navTab).remove(); //remove tab-content
                $navTab.parent("li").remove(); //remove navtab
            });
            if (retainTab) {
                self.active($el.navPanelList.find('a[data-index="' + retainTab + '"]'));
                self.activeMenu(
                    $el.navPanelList.find('a[data-index="' + retainTab + '"]')
                );
            }
            $el.navPanelList.css("margin-left", "0");
            return self;
        },


        /**
         * close all tabs, (except main tab)
         * @return self     Chain structure.
         */
        closeAll: function () {
            var self = this,
                $el = self.$element;
            $el.navPanelList.find('a:not([data-type="main"])').each(function () {
                var $navTab = $(this);
                self._delStorage($navTab.attr("data-id")); //remove tab from session storage
                self._getTabPane($navTab).remove(); //remove tab-content
                $navTab.parent("li").remove(); //remove navtab
            });
            self.active(
                $el.navPanelList.find('a[data-type="main"]:first').parent("li")
            );
            self.activeMenu($el.navPanelList.find('a[data-type="main"]:first'));
            return self;
        },

        /**
         * 左侧导航变化
         */
        activeMenu: function (navTab) {
            // 点击选项卡时，左侧菜单栏跟随变化
            var $navObj = $("a[href$='" + $(navTab).data("url") + "']"), // 当前url对应的左侧导航对象
                $navHasSubnav = $navObj.parents(".nav-item"),
                $viSubHeight = $navHasSubnav
                    .siblings()
                    .find(".nav-subnav:visible")
                    .outerHeight();

            $(".nav-item").each(function (i) {
                if (
                    $(this).hasClass("active") &&
                    !$navObj.parents(".nav-item").last().hasClass("active")
                ) {
                    $(this).removeClass("active").removeClass("open");
                    $(this).find(".nav-subnav:visible").slideUp(500);
                    if (
                        window.innerWidth > 1024 &&
                        $("body").hasClass("lyear-layout-sidebar-close")
                    ) {
                        $(this).find(".nav-subnav").hide();
                    }
                }
            });

            $(".nav-drawer").find("li").removeClass("active");
            $navObj.parent("li").addClass("active");
            $navHasSubnav.first().addClass("active");

            // 当前菜单无子菜单
            if (!$navObj.parents(".nav-item").first().is(".nav-item-has-subnav")) {
                var hht =
                    48 * ($navObj.parents(".nav-item").first().prevAll().length - 1);
                $(".lyear-layout-sidebar-scroll").animate({scrollTop: hht}, 300);
            }

            if ($navObj.parents("ul.nav-subnav").last().is(":hidden")) {
                $navObj
                    .parents("ul.nav-subnav")
                    .last()
                    .slideDown(500, function () {
                        $navHasSubnav.last().addClass("open");
                        var scrollHeight = 0,
                            $scrollBox = $(".lyear-layout-sidebar-scroll"),
                            pervTotal = $navHasSubnav.last().prevAll().length,
                            boxHeight = $scrollBox.outerHeight(),
                            innerHeight = $(".sidebar-main").outerHeight(),
                            thisScroll = $scrollBox.scrollTop(),
                            thisSubHeight = $(this).outerHeight(),
                            footHeight = 121;

                        if (footHeight + innerHeight - boxHeight >= pervTotal * 48) {
                            scrollHeight = pervTotal * 48;
                        }
                        if ($navHasSubnav.length == 1) {
                            $scrollBox.animate({scrollTop: scrollHeight}, 300);
                        } else {
                            // 子菜单操作
                            if (typeof $viSubHeight != "undefined" && $viSubHeight != null) {
                                scrollHeight = thisScroll + thisSubHeight - $viSubHeight;
                                $scrollBox.animate({scrollTop: scrollHeight}, 300);
                            } else {
                                if (thisScroll + boxHeight - $scrollBox[0].scrollHeight == 0) {
                                    scrollHeight = thisScroll - thisSubHeight;
                                    $scrollBox.animate({scrollTop: scrollHeight}, 300);
                                }
                            }
                        }
                    });
            }
        },

        /**
         * init function
         * @param options
         * @returns self
         * @private
         */
        _init: function (options) {
            var self = this,
                $el = self.$element;
            $el.html(
                defaultLayoutTemplates[options.nav.layout]
                    .replace("{mainClass}", options.class)
                    .replace("{navClass}", options.nav.class)
                    .replace(/\{nav-tabs\}/g, options.nav.style)
                    .replace(/\{backgroundColor\}/g, options.nav.backgroundColor)
                    .replace("{dropdown}", options.language.nav.dropdown)
                    .replace("{closeAllTabs}", options.language.nav.closeAllTabs)
                    .replace("{closeOtherTabs}", options.language.nav.closeOtherTabs)
            );
            $el.wrapper = $el.find(".mt-wrapper:first");
            $el.nav = $el.find(".mt-nav-bar:first");
            $el.navToolsLeft = $el.nav.find(".mt-nav-tools-left:first");
            $el.navPanel = $el.nav.find(".mt-nav-panel:first");
            $el.navPanelList = $el.nav.find(".mt-nav-panel:first ul");
            $el.navToolsRight = $el.nav.find(".mt-nav-tools-right:first");
            $el.tabContent = $el.find(".tab-content:first");
            if (options.nav.maxTabs <= 1) {
                options.nav.maxTabs = 1;
                $el.nav.hide();
            }

            $el.navPanel.css("width", "calc(100% - 132px)");
            self.options = options;
            return self;
        },

        /**
         * final funcion for after init Multitabs
         * @returns self
         * @private
         */
        _final: function () {
            var self = this,
                $el = self.$element,
                options = self.options,
                storage,
                init = options.init,
                param;
            if (supportStorage(options.cache)) {
                storage = self._storage();
                self._resetStorage({});
                $.each(storage, function (k, v) {
                    self.create(v, false);
                });
            }
            if ($.isEmptyObject(storage)) {
                init =
                    !$.isEmptyObject(init) && init instanceof Array ? init : defaultInit;
                for (var i = 0; i < init.length; i++) {
                    param = self._getParam(init[i]);
                    if (param) {
                        self.create(param);
                    }
                }
            }
            //if no any tab actived, active the main tab
            if (!$el.navPanelList.children("li.active").length) {
                self.active($el.navPanelList.find('[data-type="main"]:first'));
            }
            return self;
        },

        /**
         * bind action
         * @return self
         * @private
         */
        _listen: function () {
            var self = this,
                $el = self.$element,
                options = self.options;
            //create tab
            handler($(document), "click", options.selector, function () {
                self.create(this, true);
                if (!$(this).parent().parent("ul").hasClass("dropdown-menu")) {
                    // 20190402改，下拉菜单中的网址采用data-url，并且不阻止后面的动作
                    return false; //Prevent the default selector action
                }
            });
            //active tab
            handler($el.nav, "click", ".mt-nav-tab", function () {
                self.active(this);

                self.activeMenu(this);
            });

            //drag tab
            if (options.nav.draggable) {
                handler($el.navPanelList, "mousedown", ".mt-nav-tab", function (event) {
                    var $navTab = $(this),
                        $navTabLi = $navTab.closest("li");
                    var $prevNavTabLi = $navTabLi.prev();
                    var dragMode = true,
                        moved = false,
                        isMain = $navTab.data("type") === "main";
                    var tmpId = "mt_tmp_id_" + new Date().getTime(),
                        navTabBlankHtml =
                            '<li id="' +
                            tmpId +
                            '" class="mt-dragging" style="width:' +
                            $navTabLi.outerWidth() +
                            "px; height:" +
                            $navTabLi.outerHeight() +
                            'px;"><a style="width: 100%;  height: 100%; "></a></li>';
                    var abs_x =
                        event.pageX - $navTabLi.offset().left + $el.nav.offset().left;
                    $navTabLi.before(navTabBlankHtml);
                    $navTabLi.addClass("mt-dragging mt-dragging-tab").css({
                        left: event.pageX - abs_x + "px",
                    });

                    $(document)
                        .on("mousemove", function (event) {
                            if (dragMode && !isMain) {
                                $navTabLi.css({
                                    left: event.pageX - abs_x + "px",
                                });
                                $el.navPanelList
                                    .children('li:not(".mt-dragging")')
                                    .each(function () {
                                        var leftWidth =
                                            $(this).offset().left + $(this).outerWidth() + 20; //20 px more for gap
                                        if (leftWidth > $navTabLi.offset().left) {
                                            if ($(this).next().attr("id") !== tmpId) {
                                                moved = true;
                                                $prevNavTabLi = $(this);
                                                $("#" + tmpId).remove();
                                                $prevNavTabLi.after(navTabBlankHtml);
                                            }
                                            return false;
                                        }
                                    });
                            }
                        })
                        .on("selectstart", function () {
                            //disable text selection
                            if (dragMode) {
                                return false;
                            }
                        })
                        .on("mouseup", function () {
                            if (dragMode) {
                                $navTabLi
                                    .removeClass("mt-dragging mt-dragging-tab")
                                    .css({left: "auto"});
                                if (moved) {
                                    $prevNavTabLi.after($navTabLi);
                                }
                                $("#" + tmpId).remove();
                            }
                            dragMode = false;
                        });
                });
            }

            //close tab(点击标签旁的x)
            handler($el.nav, "click", ".mt-close-tab", function () {
                self.close($(this).closest("li"));
                return false; //Avoid possible BUG
            });


            //close all tabs
            handler($el.nav, "click", ".mt-close-all-tabs", function () {
                self.closeAll();
                //return false; //Avoid possible BUG
            });
            //close other tabs
            handler($el.nav, "click", ".mt-close-other-tabs", function () {
                self.closeOthers();
                //return false; //Avoid possible BUG
            });

            //fixed the nav-bar
            var navHeight = $el.nav.outerHeight();
            $el.tabContent.css("paddingTop", navHeight);
            if (options.nav.fixed) {
                handler($(window), "scroll", function () {
                    var scrollTop = $(this).scrollTop();
                    scrollTop =
                        scrollTop < $el.wrapper.height() - navHeight
                            ? scrollTop + "px"
                            : "auto";
                    $el.nav.css("top", scrollTop);
                    return false; //Avoid possible BUG
                });
            }

            //if layout === 'classic' show hide list in dropdown menu
            if (options.nav.layout === "classic") {
                handler($el.nav, "click", ".mt-dropdown:not(.open)", function () {
                    //just trigger when dropdown not open.
                    var list = self._getHiddenList();
                    var $dropDown = $el.navToolsRight
                        .find(".mt-hidden-list:first")
                        .empty();
                    if (list) {
                        //when list is not empty
                        while (list.prevList.length) {
                            $dropDown.append(list.prevList.shift().clone());
                        }
                        while (list.nextList.length) {
                            $dropDown.append(list.nextList.shift().clone());
                        }
                    } else {
                        $dropDown.append("<li>empty</li>");
                    }
                    // return false; //Avoid possible BUG
                });
            }
            return self;
        },

        /**
         * get the multitabs object's param
         * @param obj          multitabs's object
         * @returns param      param
         * @private
         */
        _getParam: function (obj) {
            if ($.isEmptyObject(obj)) {
                return false;
            }
            var self = this,
                options = self.options,
                param = {},
                $obj = $(obj),
                data = $obj.data();

            //content
            param.content = data.content || obj.content || "";

            if (!param.content.length) {
                //url
                param.url =
                    data.url || obj.url || $obj.attr("href") || $obj.attr("url") || "";
                param.url = $.trim(decodeURIComponent(param.url.replace("#", "")));
            } else {
                param.url = "";
            }
            if (!param.url.length && !param.content.length) {
                return false;
            }

            //iframe
            param.iframe =
                data.iframe || obj.iframe || isExtUrl(param.url) || options.iframe;
            //type
            param.type = data.type || obj.type || options.type;
            //title
            param.title =
                data.title ||
                obj.title ||
                $obj.text() ||
                param.url.replace("http://", "").replace("https://", "") ||
                options.language.nav.title;
            param.title = trimText(param.title, options.nav.maxTitleLength);
            //active
            param.active = data.active || obj.active || false;
            //index
            param.index =
                data.index || obj.index || getTabIndex(param.type, options.nav.maxTabs);
            //id
            param.did = data.did || obj.did || this._generateId(param);
            return param;
        },

        /**
         * session storage for tab list
         * @param key
         * @param param
         * @returns storage
         * @private
         */
        _storage: function (key, param) {
            if (supportStorage(this.options.cache)) {
                var storage = JSON.parse(sessionStorage.multitabs || "{}");
                if (!key) {
                    return storage;
                }
                if (!param) {
                    return storage[key];
                }
                storage[key] = param;
                sessionStorage.multitabs = JSON.stringify(storage);
                return storage;
            }
            return {};
        },

        /**
         * delete storage by key
         * @param key
         * @private
         */
        _delStorage: function (key) {
            if (supportStorage(this.options.cache)) {
                var storage = JSON.parse(sessionStorage.multitabs || "{}");
                if (!key) {
                    return storage;
                }
                delete storage[key];
                sessionStorage.multitabs = JSON.stringify(storage);
                return storage;
            }
            return {};
        },

        /**
         * reset storage
         * @param storage
         * @private
         */
        _resetStorage: function (storage) {
            if (supportStorage(this.options.cache) && typeof storage === "object") {
                sessionStorage.multitabs = JSON.stringify(storage);
            }
        },

        /**
         * check if exist multitabs obj
         * @param param
         * @private
         */
        _exist: function (param) {
            if (!param || !param.url) {
                return false;
            }
            var self = this,
                $el = self.$element;
            var $navTab = $el.navPanelList.find(
                'a[data-url="' + param.url + '"]:first'
            );
            if ($navTab.length) {
                return $navTab;
            } else {
                return false;
            }
        },

        /**
         * get tab-pane from tab
         * @param tab
         * @returns {*}
         * @private
         */
        _getTabPane: function (navTab) {
            return $("#" + $(navTab).attr("data-id"));
        },

        /**
         * get real navTab in the panel list.
         * @param navTab
         * @returns navTab
         * @private
         */
        _getNavTab: function (navTab) {
            var self = this,
                $el = self.$element;
            var dataId =
                $(navTab).attr("data-id") || $(navTab).find("a").attr("data-id");
            return $el.navPanelList.find('a[data-id="' + dataId + '"]:first');
        },

        /**
         * fix nav navTab position
         * @param navTab
         * @private
         */
        _fixTabPosition: function (navTab) {
            var self = this,
                $el = self.$element,
                $navTabLi = $(navTab).parent("li"),
                tabWidth = $navTabLi.outerWidth(true),
                prevWidth = $navTabLi.prev().outerWidth(true),
                pprevWidth = $navTabLi.prev().prev().outerWidth(true),
                sumPrevWidth = sumDomWidth($navTabLi.prevAll()),
                sumNextWidth = sumDomWidth($navTabLi.nextAll()),
                navPanelWidth = $el.navPanel.outerWidth(true),
                sumTabsWidth = sumDomWidth($el.navPanelList.children("li")),
                leftWidth = 0;
            //all nav navTab's width no more than nav-panel's width
            if (sumTabsWidth < navPanelWidth) {
                leftWidth = 0;
            } else {
                //when navTab and his right tabs sum width less or same as nav-panel, it means nav-panel can contain the navTab and his right tabs
                if (prevWidth + tabWidth + sumNextWidth <= navPanelWidth) {
                    leftWidth = sumPrevWidth; //sum width of left part
                    //add width from the left, calcular the maximum tabs can contained by nav-panel
                    while (sumTabsWidth - leftWidth + prevWidth < navPanelWidth) {
                        $navTabLi = $navTabLi.prev(); //change the left navTab
                        leftWidth -= $navTabLi.outerWidth(); //reduce the left part width
                    }
                } else {
                    //nav-panel can not contain the navTab and his right tabs
                    //when the navTab and his left part tabs's sum width more than nav-panel, all the width of 2 previous tabs's width set as the nav-panel margin-left.
                    if (sumPrevWidth + tabWidth > navPanelWidth) {
                        leftWidth = sumPrevWidth - prevWidth - pprevWidth;
                    }
                }
            }
            leftWidth = leftWidth > 0 ? leftWidth : 0; //avoid leftWidth < 0 BUG
            $el.navPanelList.animate(
                {
                    marginLeft: 0 - leftWidth + "px",
                },
                "fast"
            );
        },

        /**
         * hidden tab list
         * @returns hidden tab list, the prevList and nextList
         * @private
         */
        _getHiddenList: function () {
            var self = this,
                $el = self.$element,
                navPanelListMarginLeft = Math.abs(
                    parseInt($el.navPanelList.css("margin-left"))
                ),
                navPanelWidth = $el.navPanel.outerWidth(true),
                sumTabsWidth = sumDomWidth($el.navPanelList.children("li")),
                tabPrevList = [],
                tabNextList = [],
                $navTabLi,
                marginLeft;
            //all tab's width no more than nav-panel's width
            if (sumTabsWidth < navPanelWidth) {
                return false;
            } else {
                $navTabLi = $el.navPanelList.children("li:first");
                //overflow hidden left part
                marginLeft = 0;
                //from the first tab, add all left part hidden tabs
                while (marginLeft + $navTabLi.width() <= navPanelListMarginLeft) {
                    marginLeft += $navTabLi.outerWidth(true);
                    tabPrevList.push($navTabLi);
                    $navTabLi = $navTabLi.next();
                }
                //overflow hidden right part
                if (sumTabsWidth > marginLeft) {
                    //check if the right part have hidden tabs
                    $navTabLi = $el.navPanelList.children("li:last");
                    marginLeft = sumTabsWidth; //set margin-left as the Rightmost, and reduce one and one.
                    while (marginLeft > navPanelListMarginLeft + navPanelWidth) {
                        marginLeft -= $navTabLi.outerWidth(true);
                        tabNextList.unshift($navTabLi); //add param from top
                        $navTabLi = $navTabLi.prev();
                    }
                }
                return {
                    prevList: tabPrevList,
                    nextList: tabNextList,
                };
            }
        },

        /**
         * check if tab-pane is iframe, and add/remove class
         * @param tabPane
         * @private
         */
        _fixTabContentLayout: function (tabPane) {
            var $tabPane = $(tabPane);
            if ($tabPane.is("iframe")) {
                $("body").addClass("full-height-layout");
                /** fix chrome croll disappear bug **/
                $tabPane.css("height", "99%");
                window.setTimeout(function () {
                    $tabPane.css("height", "100%");
                }, 0);
            } else {
                $("body").removeClass("full-height-layout");
            }
        },
    };

    /**
     * Entry function
     * @param option
     */
    $.fn.multitabs = function (option, id) {
        var self = $(this),
            did = id ? id : "multitabs",
            multitabs = $(document).data(did),
            options = typeof option === "object" && option,
            opts;
        if (!multitabs) {
            opts = $.extend(true, {}, $.fn.multitabs.defaults, options, self.data());
            opts.nav.style =
                opts.nav.style === "nav-pills" ? "nav-pills" : "nav-tabs";
            multitabs = new MultiTabs(this, opts);
            $(document).data(did, multitabs);
        }
        return $(document).data(did);
    };

    /**
     * Default Options
     * @type {}
     */
    $.fn.multitabs.defaults = {
        selector: ".multitabs", //selector text to trigger multitabs.
        iframe: false, //Global iframe mode, default is false, is the auto mode (for the self page, use ajax, and the external, use iframe)
        cache: false,
        class: "", //class for whole multitabs
        type: "info", //change the info content name, is not necessary to change.
        init: [],
        refresh: false,
        nav: {
            backgroundColor: "#f5f5f5", //default nav-bar background color
            class: "", //class of nav
            draggable: true, //nav tab draggable option
            fixed: false, //fixed the nav-bar
            layout: "default", //it can be 'default', 'classic' (all hidden tab in dropdown list), and simple
            maxTabs: 15, //Max tabs number (without counting main tab), when is 1, hide the whole nav
            maxTitleLength: 25, //Max title length of tab
            showCloseOnHover: false, //while is true, show close button in hover, if false, show close button always
            style: "nav-tabs", //can be nav-tabs or nav-pills
        },
        content: {
            ajax: {
                class: "", //Class for ajax tab-pane
                error: function (htmlCallBack) {
                    //modify html and return
                    return htmlCallBack;
                },
                success: function (htmlCallBack) {
                    //modify html and return
                    return htmlCallBack;
                },
            },
            iframe: {
                class: "",
            },
        },
        language: {
            //language setting
            nav: {
                title: "Tab", //default tab's tittle
                dropdown: '<i class="mdi mdi-menu"></i>', //right tools dropdown name
                closeAllTabs: "关闭所有标签页", //close all tabs
                closeOtherTabs: "关闭其他标签页", //close other tabs
            },
        },
    };
})(jQuery);
